<?php
namespace ApolloClient;

use Illuminate\Support\Arr;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class ApolloClientService
{
    //AppId
    protected string $appId;

    //链接
    protected string $server;

    //集群
    protected string $cluster;

    //密钥
    protected string $secret;

    //空间
    protected array $namespaces;

    // 是否为debug状态
    protected bool $debug;

    /**
     * TaxSuiDeService constructor.
     * @param array $config
     */
    public function __construct(array $config)
    {
        $this->appId    = data_get($config, 'app_id');
        $this->server   = rtrim(data_get($config, 'server'), '/');
        $this->cluster  = data_get($config, 'cluster');
        $this->secret   = data_get($config, 'secret');
        $this->namespaces   = explode(',', data_get($config, 'namespace'));
        $this->debug = (bool)data_get($config, 'debug');
    }

    /**
     * @return void
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function handle()
    {
        $this->doSync();
    }

    /**
     * @param string $namespace
     * @param string $time
     * @return string
     */
    protected function sign(string $namespace, string $time): string
    {
        $query = "/configs/{$this->appId}/{$this->cluster}/{$namespace}"
            . "?releaseKey={$this->releaseKey($namespace)}";
        $data = implode("\n", [$time, $query]);
        $sign = base64_encode(hash_hmac('sha1', $data, $this->secret, true));
        return $sign;
    }

    /**
     * @param string $namespace
     * @return string
     */
    protected function url(string $namespace): string
    {
        return "{$this->server}/configs/{$this->appId}/{$this->cluster}/{$namespace}"
            . "?releaseKey={$this->releaseKey($namespace)}";
    }

    /**
     * @param string $namespace
     * @return string
     */
    protected function setReleaseKey(string $namespace, string $releaseKey): void
    {
        if ($this->debug) {

            return;
        }
        $cacheKey = $this->cacheKey($namespace);
        if (! cache([$cacheKey => $releaseKey])) {

            throw new ApolloClientException('cache releaseKey failed');
        }
    }

    /**
     * @param string $namespace
     * @param string $default
     * @return string
     * @throws \Exception
     */
    protected function releaseKey(string $namespace, string $default = ''): string
    {
        if ($this->debug) {

            return '';
        }
        $cacheKey = $this->cacheKey($namespace);
        $data = cache($cacheKey, $default);
        return $data;
    }

    /**
     * @param string $namespace
     * @return string
     */
    protected function cacheKey(string $namespace)
    {
        return implode('===', [
            $this->appId,
            $this->cluster,
            $namespace
        ]);
    }

    /**
     * @param string $namespace
     * @return array
     */
    protected function header(string $namespace)
    {
        if (empty($this->secret)) {

            return [];
        }
        $time = sprintf('%.0f', microtime('string') * 1000);
        $sign = $this->sign($namespace, $time);
        return [
            'Authorization' => "Apollo {$this->appId}:{$sign}",
            'Timestamp' => $time,
        ];
    }

    /**
     * 同步
     * @return void
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    protected function doSync()
    {
        try {
            //获取毫秒级时间戳
            $cfgs = collect();
            collect($this->namespaces)->map(function ($namespace) use (&$cfgs) {

                $url = $this->url($namespace);
                $header = $this->header($namespace);
                dump($url, $header);
                $client = new \GuzzleHttp\Client(['timeout' => 3.00, 'headers' => $header]);
                $response = $client->request('GET', $url);
                if ($response->getStatusCode() == 304) {
                    return [];
                }
                $body = json_decode($response->getBody()->getContents(), true);
                $releaseKey = data_get($body, 'releaseKey', '');
                $this->setReleaseKey($namespace, $releaseKey);
                $cfg = Arr::get($body, 'configurations', []);
                $cfgs = $cfgs->merge($cfg);
            });
            if ($cfgs->isNotEmpty()) {
                foreach ($_ENV as $key => $value) {
                    if(isset($cfgs[$key])){
                        $cfgs[$key] = $cfgs[$key] ?? $_ENV[$key];
                    }
                }
                $items = [];
                foreach ($cfgs as $key => $value) {
                    data_set($items, $key, $value);
                }
                $content = '';
                foreach (Arr::dot($items) as $k => $item) {
                    dump('Saving [' . $k . ']');
                    $content .= $k . '=' . $item . "\n";
                }
                $fileName = base_path('.env');
                $fileName_back = base_path('.env_back_' . now()->format('YmdHis'));
                if ($content) {
                    File::put($fileName_back, File::get($fileName), true);
                    File::put($fileName, $content, true);
                }
            }

        } catch (\Exception $e) {
            dump($e);
            report($e);
        }
    }
}
